/////////////////////////////////////////////////////////////////////////
// 词法分析
// 本节没有提供词法分析器，直接提供了一个Token串。
// 语法分析程序可以从Token串中依次读出一个个Token，也可以重新定位Token串的当前读取位置。

use std::fmt;

//Token的类型
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub enum TokenKind{
	Keyword,
	Identifier,
	StringLiteral,
	Seperator,
	Operator,
	EOF
}

impl PartialEq for TokenKind {
    fn eq(&self, other :&TokenKind) -> bool {
        *self as i32 == *other as i32
    }
}

const TOKE_KIND_NAME: [&'static str; 6] = ["Keyword", "Identifier", "StringLiteral", "Seperator", "Operator", "EOF",];
impl fmt::Display for TokenKind{
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", TOKE_KIND_NAME[*self as usize])
    }
}

// 代表一个Token的数据结构
pub struct Token  {
	pub kind: TokenKind,
	pub text: String
}

impl fmt::Display for Token{
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "(Kind={}, Text={})", self.kind, self.text)
    }
}

impl Clone for Token{
	fn clone(&self) -> Token{
		Token::new(self.kind.clone(), self.text.clone())
	}
}

impl Token{
	pub fn new(kind: TokenKind, text: String) -> Token {
		Token{
			kind: kind,
			text: text
		}
	}
}

/**
 * 简化的词法分析器
	* 语法分析器从这里获取Token。
	* 支持下面两个操作：
	* next(): 返回当前的Token，并移向下一个Token。
	* peek(): 返回当前的Token，但不移动当前位置。
	*/
pub struct Tokenizer  {
	tokens: Vec<Token>,
	pos:    usize
}

impl Tokenizer{
	pub fn new(tokens: Vec<Token>) -> Tokenizer {
		Tokenizer{
			pos:    0,
			tokens: tokens
		}
	}

	//获取下一个token，pos+1
	pub fn next(&mut self) -> Token {
		if self.pos < self.tokens.len() {
			self.pos += 1;
			return self.tokens[self.pos-1].clone();
		} else {
			//如果已经到了末尾，总是返回EOF
			return self.tokens[self.pos].clone();
		}
	}

	//获取下一个token，但不修改pos
	pub fn peek(&self) ->  Token {
		if self.pos < self.tokens.len() {
			return self.tokens[self.pos].clone();
		} else {
			//如果已经到了末尾，总是返回EOF
			return self.tokens[self.pos].clone();
		}
	}

	pub fn position(&self) -> usize {
		return self.pos;
	}

	pub fn trace_back(&mut self, new_pos: usize) {
		self.pos = new_pos;
	}
}
